/*
 * Ethan Levine
 * 
 * The contents of this file are subject to the GNU General Public
 * License (GPL) v2.  The full contents of this license can be
 * found at Mathma's website, http://www.codeplex.com/mathma/license
 */

package mathma.core.calc;

import mathma.core.form.SuspendedExpression;
import mathma.core.form.UnknownSuspensionException;
import mathma.core.real.Real;
import mathma.core.expr.Variable;
import mathma.core.expr.Expression;
import mathma.core.expr.VariableMapping;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>Represents a definite integral.  A definite integral can be viewed as the
 * area under the graph of a function from point a to point b.  In this case,
 * the function body is <code>operand</code>, the integration variable is
 * <code>var</code>, point a is the <code>low</code>, and point b is the
 * <code>high</code>.</p>
 *
 * <p>DefiniteIntegralExpressions are suspended, and thus require work before a
 * value can be obtained.  For more information about this process, see
 * {@link mathma.core.form.SuspendedExpression}.</p>
 *
 * ** NYI **
 * 
 * @since 0.1.4
 * @author Ethan Levine
 */
public class DefiniteIntegralExpression extends SuspendedExpression<Real, Real> {

    private Expression<Real> operand;
    private Variable<Real> var;
    private Real low;
    private Real high;

    /**
     * Creates a new definite integral expression using the given parameters.
     * 
     * @param operand The body of the integral.
     * @param var The integration variable.
     * @param low Where to start integration.
     * @param high Where to end integration.
     */
    public DefiniteIntegralExpression(Expression<Real> operand,
            Variable<Real> var, Real low, Real high) {
        super(Real.Type);
        this.operand = operand;
        this.var = var;
        this.low = low;
        this.high = high;
    }

    /**
     * Returns the body of this definite integral.
     *
     * @return The body of this definite integral.
     */
    public Expression<Real> getOperand() {
        return operand;
    }

    /**
     * Returns the integration variable of this definite integral.
     *
     * @return The integration variable of this definite integral.
     */
    public Variable<Real> getVariable() {
        return var;
    }

    /**
     * Returns the "low" or beginning point of this definite integral.
     *
     * @return The low of this integral.
     */
    public Real getLow() {
        return low;
    }

    /**
     * Returns the "high" or ending point of this definite integral.
     *
     * @return The high of this integral.
     */
    public Real getHigh() {
        return high;
    }

    @Override
    public Expression<Real> calculate() throws UnknownSuspensionException {
        // TODO - implement
        return null;
    }
    
    public DefiniteIntegralExpression newInstance(List<Expression<Real>> operands) {
        // List must be of form:
        // (Expression<Real>, Variable<Real>, Real, Real)
        if (operands.size() == 4
                && Variable.class.isAssignableFrom(operands.get(1).getClass())
                && Real.class.isAssignableFrom(operands.get(2).getClass())
                && Real.class.isAssignableFrom(operands.get(3).getClass())) {
            return new DefiniteIntegralExpression(operands.get(0),
                    (Variable<Real>) operands.get(1),
                    (Real) operands.get(2), (Real) operands.get(3));
        } else {
            throw new IllegalArgumentException(
                    "Improper arguments to DefiniteIntegralExpression." +
                    "newInstance(List<Expression<Value>>)");
        }
    }
    
    public List<Expression<Real>> getOperands() {
        List<Expression<Real>> operands = new ArrayList<Expression<Real>>();
        operands.add(operand);
        operands.add(var);
        operands.add(low);
        operands.add(high);
        return operands;
    }

    @Override
    public Expression<Real> differentiate(Variable<Real> var) {
        try {
            return calculate().differentiate(var);
        } catch (UnknownSuspensionException e) {
            return new DerivativeExpression(this, var);
        }
    }
}
